package test.config;

import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import cn.demoncat.util.mybatis.constant.DataSourceTypeConstant;
import cn.demoncat.util.mybatis.util.RwRoutingDataSource;

/**
 * 描述：mybatis配置读写分离示例
 *
 * @author 延晓磊
 *
 * @since 2017年7月21日
 */
// 扫描Mapper包
//@MapperScan({ "cn.**.mapper", "com.**.mapper" })
// 支持事务注解
//@EnableTransactionManagement
// 配置类(spring.xml-beans)
//@Configuration
public class MyBatisConfig {

	// 读库数量
	private static final int readDataSourceNum = 2;
	// 负载均衡类型：0 随机，1轮循
	private static final int balanceType = 1;

	// Mapper配置的路径
	private static final String MAPPER_LOCATION = "classpath*:/mapper/data/*Mapper.xml";

	/**
	 * 数据源类型
	 */
	@Value("${datasource.type}")
	private Class<? extends DataSource> dataSourceType;

	/**
	 * 写库(直接读取配置创建DataSource，但可能属性没有完全注入，需要手动new)
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年10月13日
	 */
	@Bean(name = "writeDataSource")
	@Primary
	@ConfigurationProperties(prefix = "datasource.write")
	public DataSource writeDataSource() {
		return DataSourceBuilder.create().type(dataSourceType).build();
	}

	/**
	 * 读库0(数据源索引从0开始)
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年10月13日
	 */
	@Bean(name = "readDataSource0")
	@ConfigurationProperties(prefix = "datasource.read0")
	public DataSource readDataSource0() {
		return DataSourceBuilder.create().type(dataSourceType).build();
	}

	/**
	 * 读库1
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年10月13日
	 */
	@Bean(name = "readDataSource1")
	@ConfigurationProperties(prefix = "datasource.read1")
	public DataSource readDataSource1() {
		return DataSourceBuilder.create().type(dataSourceType).build();
	}

	/**
	 * 整合多库的路由数据源
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年10月13日
	 */
	@Bean(name = "rwRoutingDataSource")
	public AbstractRoutingDataSource rwRoutingDataSource(
			@Qualifier("writeDataSource") DataSource writeDataSource,
			@Qualifier("readDataSource0") DataSource readDataSource0,
			@Qualifier("readDataSource1") DataSource readDataSource1) {
		// 整合多库 ，读库索引从0开始
		Map<Object, Object> ds = new HashMap<>();
		ds.put(DataSourceTypeConstant.WRITE, writeDataSource);
		ds.put(DataSourceTypeConstant.READ + 0, readDataSource0);
		ds.put(DataSourceTypeConstant.READ + 1, readDataSource1);
		// 创建路由数据源
		RwRoutingDataSource dataSource = new RwRoutingDataSource(readDataSourceNum, balanceType);
		// 默认使用写库
		dataSource.setDefaultTargetDataSource(writeDataSource);
		// 设置多数据源
		dataSource.setTargetDataSources(ds);
		return dataSource;
	}

	/**
	 * sqlSessionFactory
	 * 
	 * @param rwRoutingDataSource
	 * @return
	 * @throws Exception
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月5日
	 */
	@Bean(name = "sqlSessionFactory")
	@Primary
	public SqlSessionFactory sqlSessionFactory(@Qualifier("rwRoutingDataSource") DataSource rwRoutingDataSource)
			throws Exception {
		final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
		sessionFactory.setDataSource(rwRoutingDataSource);
		// 指定XML配置地址
		sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATION));
		return sessionFactory.getObject();
	}

	/**
	 * 事物管理器
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月5日
	 */
	@Bean(name = "transactionManager")
	@Primary
	public DataSourceTransactionManager transactionManager(@Qualifier("writeDataSource") DataSource writeDataSource) {
		return new DataSourceTransactionManager(writeDataSource);
	}
}